﻿using Library.Common.Serialize;
using Library.Core.Domain.Repository;
using Library.Core.Infrastructure.PersistenceObject;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;

namespace Library.Core.Infrastructure.Common
{
    public static class RepositoryExtension
    {

        /// <summary>
        /// 果条件成立，就过滤
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="linq"></param>
        /// <param name="condition"></param>
        /// <param name="expression"></param>
        /// <returns></returns>
        public static IQueryable<T> WhereIf<T>(this IQueryable<T> linq, bool condition, Expression<Func<T, bool>> expression)
        {
            if (!condition)
            {
                return linq;
            }
            return linq.Where(expression);
        }

        /// <summary>
        /// 如果条件成立，就过滤
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="condition"></param>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public static IEnumerable<T> WhereIf<T>(this IEnumerable<T> source, bool condition, Func<T, bool> predicate)
        {
            return condition
                ? source.Where(predicate)
                : source;
        }

        public static IQueryable<TSource> WhereInIf<TSource, TKey>(this IQueryable<TSource> source, bool condition, IEnumerable<TKey> source2,
            Expression<Func<TSource, TKey>> keySelector)
        {
            return condition ? source.WhereIn(source2, keySelector) : source;
        }

        public static IQueryable<TSource> WhereIn<TSource, TKey>(this IQueryable<TSource> source, IEnumerable<TKey> source2,
             Expression<Func<TSource, TKey>> keySelector)
        {
            Expression inExpression = GetInExpression(source2, keySelector);
            return source.Where(Expression.Lambda<Func<TSource, bool>>(inExpression, keySelector.Parameters));
        }

        private static Expression GetInExpression<TSource, TKey>(IEnumerable<TKey> source2,
               Expression<Func<TSource, TKey>> keySelector)
        {
            Type genericType = typeof(List<>).MakeGenericType(typeof(TKey));
            var conExp = Expression.Constant(source2.ToList(), genericType);

            MethodInfo methodInfo = genericType.GetMethods().FirstOrDefault(m => m.Name.Equals("Contains")
                && m.GetParameters().Count() == 1 && m.GetParameters().FirstOrDefault().ParameterType == typeof(TKey));
            var bodyExp = Expression.Call(conExp, methodInfo, keySelector.Body);
            return bodyExp;
        }

        private readonly static MethodInfo ContainsMethod = typeof(Enumerable).GetMethods()
            .Where(x => x.IsStatic && x.IsGenericMethod && x.Name == "Contains" && x.GetParameters().Length == 2)
            .FirstOrDefault();

        public static IQueryable<T> WhereInQuery<T, TKey>(this IQueryable<T> linq, Expression<Func<T, TKey>> keySelector, IQueryable<TKey> keyQuery)
        {
            var method = ContainsMethod.MakeGenericMethod(typeof(TKey));
            var constant = Expression.Constant(keyQuery, keyQuery.GetType());
            var entity = Expression.Parameter(typeof(T), "e");
            var member = (MemberExpression)keySelector.Body;
            var key = Expression.Property(entity, member.Member.Name);
            var any = Expression.Call(method, constant, key);
            var filter = Expression.Lambda<Func<T, bool>>(any, entity);
            return linq.Where(filter);
        }

        public static void LogicRemoveRange<T>(this DbSet<T> store, Expression<Func<T, bool>> expression, Guid? userId, string userName) where T : PersistenceObjectBase
        {
            var models = store.Where(expression).Where(x => !x.IsDeleted).ToList();
            foreach (var item in models)
            {
                item.IsDeleted = true;
                item.DelTime = DateTime.Now;
                item.DelUserName = userName;
                item.DelUserId = userId;
                store.Update(item);
            }
        }

        /// <summary>
        /// 是否有值
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static bool HasValue(this string str)
        {
            return !string.IsNullOrEmpty(str);
        }

        /// <summary>
        /// 获取分页列表
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="query"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        public static async Task<PageData<T>> ToPagedList<T>(this IOrderedQueryable<T> query, int pageIndex, int pageSize)
        {
            var pageData = new PageData<T>();

            int quyCount = await query.CountAsync();
            if (quyCount > 0)
            {
                pageData.Total = quyCount;
                int TotalPages = (int)Math.Ceiling(pageData.Total / (double)pageSize);
                pageData.Page = pageIndex > TotalPages ? TotalPages : pageIndex;
                pageData.PageSize = pageSize;

                if (pageIndex > 0 && pageSize > 0)
                {
                    var linq = query.Skip((pageData.Page - 1) * pageSize).Take(pageSize);
#if DEBUG
                    var rs = EFCore.BulkExtensions.IQueryableExtensions.ToParametrizedSql(linq);
                    string sql = rs.Item1;
#endif

                    pageData.Rows = await linq.ToListAsync();
                }
                else
                {
                    pageData.Rows = await query.ToListAsync();
                }

            }
            else
            {
                pageData.Rows = new List<T>();
            }
            return pageData;
        }

        /// <summary>
        /// 获取分页列表
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="query"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        public static async Task<PageData<T>> ToPagedList<T>(this IQueryable<T> query, int pageIndex, int pageSize, (Expression<Func<T, dynamic>>, bool)[] orderExp)
        {
            var pageData = new PageData<T>();

            if (orderExp!=null && orderExp.Any())
            {
                foreach (var item in orderExp)
                {
                    if (item.Item2)
                    {
                        query = query.OrderBy(item.Item1);
                    }
                    else
                    {
                        query = query.OrderByDescending(item.Item1);
                    }
                }
            }
            int quyCount = await query.CountAsync();
            if (quyCount > 0)
            {
                pageData.Total = quyCount;
                int TotalPages = (int)Math.Ceiling(pageData.Total / (double)pageSize);
                pageData.Page = pageIndex > TotalPages ? TotalPages : pageIndex;
                pageData.PageSize = pageSize;
                if (pageIndex > 0 && pageSize > 0)
                {
                    var linq = query.Skip((pageData.Page - 1) * pageSize).Take(pageSize);
#if DEBUG
                    var rs = EFCore.BulkExtensions.IQueryableExtensions.ToParametrizedSql(linq);
                    string sql = rs.Item1;
#endif
                    pageData.Rows = await linq.ToListAsync();
                }
                else
                {
                    pageData.Rows = await query.ToListAsync();
                }
            }
            else
            {
                pageData.Rows = new List<T>();
            }
            return pageData;
        }

        public static PageData<TResult> MapPageList<TSource, TResult>(this PageData<TSource> page)
            where TResult : class
            where TSource : class
        {
            List<TResult> list = MapperHelper<TSource, TResult>.MapList(page.Rows);
            return new PageData<TResult>
            {
                Rows = list,
                Total = page.Total,
                Page = page.Page,
                PageSize = page.PageSize
            };
        }

        public static List<TResult> MapList<TSource, TResult>(this List<TSource> list)
           where TResult : class
           where TSource : class
        {
            return MapperHelper<TSource, TResult>.MapList(list);
        }
    }
}
